using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

using AGG3D.Graphics;
using AGG.Image;
using AGG.RasterizerScanline;

namespace AGG.UI
{
    internal class Win32BackBuffer
    {
        internal ImageBuffer m_BackBufferImage;
        internal ImageBufferFloat m_BackBufferImageFloat;
        internal Bitmap m_WidowsBitmap;

        internal void UpdateHardwareSurface()
        {
            if (m_BackBufferImage != null)
            {
                BitmapData bitmapData = m_WidowsBitmap.LockBits(new Rectangle(0, 0, m_WidowsBitmap.Width, m_WidowsBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, m_WidowsBitmap.PixelFormat);
                int backBufferStrideInBytes = m_BackBufferImage.StrideInBytes();
                int backBufferStrideInInts = backBufferStrideInBytes / 4;
                int backBufferHeight = m_BackBufferImage.Height;
                int backBufferHeightMinusOne = backBufferHeight - 1;
                int bitmapDataStride = bitmapData.Stride;
                int offset;
                byte[] buffer = m_BackBufferImage.GetBuffer(out offset);
                switch (m_BackBufferImage.BitDepth)
                {
                    case 24:
                        {
                            unsafe
                            {
                                byte* bitmapDataScan0 = (byte*)bitmapData.Scan0;
                                fixed (byte* pSourceFixed = &buffer[offset])
                                {
                                    byte* pSource = pSourceFixed;
                                    byte* pDestBuffer = bitmapDataScan0 + bitmapDataStride * backBufferHeightMinusOne;
                                    for (int y = 0; y < backBufferHeight; y++)
                                    {
                                        int* pSourceInt = (int*)pSource;
                                        int* pDestBufferInt = (int*)pDestBuffer;
                                        for (int x = 0; x < backBufferStrideInInts; x++)
                                        {
                                            pDestBufferInt[x] = pSourceInt[x];
                                        }
                                        for (int x = backBufferStrideInInts * 4; x < backBufferStrideInBytes; x++)
                                        {
                                            pDestBuffer[x] = pSource[x];
                                        }
                                        pDestBuffer -= bitmapDataStride;
                                        pSource += backBufferStrideInBytes;
                                    }
                                }
                            }
                        }
                        break;

                    case 32:
                        {
                            unsafe
                            {
                                byte* bitmapDataScan0 = (byte*)bitmapData.Scan0;
                                fixed (byte* pSourceFixed = &buffer[offset])
                                {
                                    byte* pSource = pSourceFixed;
                                    byte* pDestBuffer = bitmapDataScan0 + bitmapDataStride * backBufferHeightMinusOne;
                                    for (int y = 0; y < backBufferHeight; y++)
                                    {
                                        int* pSourceInt = (int*)pSource;
                                        int* pDestBufferInt = (int*)pDestBuffer;
                                        for (int x = 0; x < backBufferStrideInInts; x++)
                                        {
                                            pDestBufferInt[x] = pSourceInt[x];
                                        }
                                        pDestBuffer -= bitmapDataStride;
                                        pSource += backBufferStrideInBytes;
                                    }
                                }
                            }
                        }
                        break;

                    default:
                        throw new NotImplementedException();
                }
                m_WidowsBitmap.UnlockBits(bitmapData);
            }
            else
            {
                switch (m_BackBufferImageFloat.BitDepth)
                {
                    case 128:
                        {
                            BitmapData bitmapData = m_WidowsBitmap.LockBits(new Rectangle(0, 0, m_WidowsBitmap.Width, m_WidowsBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, m_WidowsBitmap.PixelFormat);
                            int index = 0;
                            unsafe
                            {
                                unchecked
                                {
                                    int offset;
                                    float[] buffer = m_BackBufferImageFloat.GetBuffer(out offset);
                                    fixed (float* pSource = &buffer[offset])
                                    {
                                        for (int y = 0; y < m_BackBufferImageFloat.Height; y++)
                                        {
                                            byte* pDestBuffer = (byte*)bitmapData.Scan0 + (bitmapData.Stride * (m_BackBufferImageFloat.Height - 1 - y));
                                            for (int x = 0; x < m_BackBufferImageFloat.Width; x++)
                                            {
#if true
                                                pDestBuffer[x * 4 + 0] = (byte)(pSource[index * 4 + 0] * 255);
                                                pDestBuffer[x * 4 + 1] = (byte)(pSource[index * 4 + 1] * 255);
                                                pDestBuffer[x * 4 + 2] = (byte)(pSource[index * 4 + 2] * 255);
                                                pDestBuffer[x * 4 + 3] = (byte)(pSource[index * 4 + 3] * 255);
                                                index++;
#else
                                                pDestBuffer[x * 4 + 0] = (byte)255;
                                                pDestBuffer[x * 4 + 1] = (byte)0;
                                                pDestBuffer[x * 4 + 2] = (byte)128;
                                                pDestBuffer[x * 4 + 3] = (byte)255;
#endif
                                            }
                                        }
                                    }
                                }
                            }

                            m_WidowsBitmap.UnlockBits(bitmapData);
                        }
                        break;

                    default:
                        throw new NotImplementedException();
                }
            }
        }

        internal void Initialize(int width, int height, int bitDepth)
        {
            if (width > 0 && height > 0)
            {
                switch (bitDepth)
                {
                    case 24:
                        m_WidowsBitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                        m_BackBufferImage = new ImageBuffer(width, height, 24, new BlenderBGR());
                        break;

                    case 32:
                        m_WidowsBitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
                        m_BackBufferImage = new ImageBuffer(width, height, 32, new BlenderBGRA());
                        break;

                    case 128:
                        m_WidowsBitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
                        m_BackBufferImage = null;
                        m_BackBufferImageFloat = new ImageBufferFloat(width, height, 128, new BlenderBGRAFloat());
                        break;

                    default:
                        throw new NotImplementedException("Don't support this bit depth yet.");
                }
            }
        }
    }
}
